import { app, BrowserWindow, ipcMain, dialog } from 'electron'
import { fileURLToPath } from 'node:url'
import path from 'node:path'
// import { runPuppeteerTask } from './puppeteer'
import excel from 'exceljs'
import Store from 'electron-store'
import puppeteer from 'puppeteer'
import type { Browser, Page } from 'puppeteer'
import { createWorker } from 'tesseract.js'
import fs from 'node:fs'
import http from 'node:http'

// const require = createRequire(import.meta.url)
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const store = new Store()

process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'

// The built directory structure
//
// ├─┬─┬ dist
// │ │ └── index.html
// │ │
// │ ├─┬ dist-electron
// │ │ ├── main.js
// │ │ └── preload.mjs
// │
process.env.APP_ROOT = path.join(__dirname, '..')

// 🚧 Use ['ENV_NAME'] avoid vite:define plugin - Vite@2.x
export const VITE_DEV_SERVER_URL = process.env['VITE_DEV_SERVER_URL']
export const MAIN_DIST = path.join(process.env.APP_ROOT, 'dist-electron')
export const RENDERER_DIST = path.join(process.env.APP_ROOT, 'dist')

process.env.VITE_PUBLIC = VITE_DEV_SERVER_URL ? path.join(process.env.APP_ROOT, 'public') : RENDERER_DIST

let win: BrowserWindow | null

function createWindow() {
  win = new BrowserWindow({
    width: 800,
    height: 800,
    resizable: false, //禁止改变主窗口尺寸
    icon: path.join(process.env.VITE_PUBLIC, 'logo.svg'),
    webPreferences: {
      preload: path.join(__dirname, 'preload.mjs'),
    },
  })

  win.webContents.on('did-finish-load', () => {
    win?.webContents.send('main-process-message', (new Date).toLocaleString())
  })

  if (VITE_DEV_SERVER_URL) {
    win.loadURL(VITE_DEV_SERVER_URL)
  } else {
    // win.loadFile('dist/index.html')
    win.loadFile(path.join(RENDERER_DIST, 'index.html'))
  }
}

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
    win = null
  }
})

app.on('activate', () => {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow()
  }
})

app.whenReady().then(createWindow)


let list: any[] = []
let alertMessage: null | string = null
let savePath = store.get<string>('savePath') as string || ''

ipcMain.on('view-readly', async (event) => {
  if (savePath) {
    const date = new Date()
    const year = date.getFullYear()
    const month = String(date.getMonth() + 1).padStart(2, '0')
    const day = String(date.getDate()).padStart(2, '0')
    const formattedDate = `${year}-${month}-${day}`
    savePath = `${savePath}/invoice-download-${formattedDate}`
    event.reply('save-path-updata', savePath)
  }
})

ipcMain.on('file-readly', async (event, filePath) => {
  // 读取Excel文件
  const workbook = new excel.Workbook()
  await workbook.xlsx.readFile(filePath)
  const worksheet = workbook.getWorksheet(1)
  list = []
  worksheet?.eachRow(async (row, _rowNumber) => {
    const [username, name, password] = (row.values.slice as any)(1);
    if (!username || !name || !password) return
    list.push({
      username,
      name,
      password,
    })
  })
  event.reply('list-readly', list)
  const browser = await puppeteer.launch({ headless: true })
  const page = await browser.newPage()
  page.on('dialog', async (dialog) => {
    alertMessage = dialog.message()
    console.log('alertMessage: ', alertMessage)
    await dialog.dismiss()  // 关闭弹窗（相当于点击“取消”或关闭按钮）
  })
  const callback = (index: number, status: string) => {
    event.reply('list-updata', {
      index,
      status
    })
  }
  for (const item of list) {
    const index = list.findIndex((i) => i.username === item.username)
    try {
      await download({
        username: item.username,
        password: item.password,
        name: item.name,
        browser,
        page,
        index,
        callback
      })
    } catch (error) {

    }
  }
  console.log('完成')
  event.reply('download-complete')
})

ipcMain.on('set-save-path', async (event) => {
  const result = await dialog.showOpenDialog({
    properties: ['openDirectory'],  // 指定选择文件夹模式
    title: '选择文件夹',
    buttonLabel: '确认'
  })
  const selectedPath = result.filePaths[0] || ''
  if (selectedPath) {
    store.set('savePath', selectedPath)
    savePath = selectedPath
    event.reply('save-path-updata', savePath)
  }
})

async function download(
  {
    username,
    password,
    name,
    page,
    browser,
    index,
    callback
  }: {
    username: string,
    password: string
    name: string
    browser?: Browser
    page: Page
    index: number
    callback: Function
  }) {
  await page.goto('https://pay.nwnu.edu.cn/login.aspx?local=zh-cn')
  let loginSuccess = false
  let retryCount = 0
  const MAX_RETRIES = 10
  console.log('index: ', index);
  callback(index, '登录中...')
  while (retryCount < MAX_RETRIES && !loginSuccess) {
    try {
      // 检查跳转后的页面是否存在登录成功后的特定元素
      const successElement = await page.$('#hyper_5')
      loginSuccess = !!successElement
      if (loginSuccess) {
        console.log('登录成功！')
        callback(index, '登录成功，查找发票并保存中...')
        const cookies = await browser?.cookies()
        let cookieString = ''
        if (cookies) {
          cookies.forEach(cookie => {
            if (cookie.name === 'ASP.NET_SessionId') {
              cookieString = `${cookie.name}=${cookie.value}; `
            }
          })
        }
        await page.evaluate(() => {
          const link = document.querySelector('#hyper_5')
          location.href = link?.getAttribute('href') || ''
        });
        await page.waitForNavigation({ waitUntil: 'networkidle0' })
        const ptid = await page.evaluate(() => {
          // 查找包含指定图片的 <a> 元素
          const link = document.querySelector('a img[src="../../images/bill.png"]')?.closest('a')
          if (!link) return null;
      
          // 提取 href 中的 ptid 参数
          const url = new URL(link.href, window.location.origin)
          return url.searchParams.get('ptid')
        })
        if (!ptid) {
          callback(index, '未找到发票')
          return 
        }
        const options = {
          hostname: 'pay.nwnu.edu.cn',
          path: 'https://pay.nwnu.edu.cn/api/user/EbillData//GetBusPicture',
          method: 'POST',
          headers: {
            'Accept': 'text/html,application/xhtml+xml',
            'Accept-Language': 'en-US,en;q=0.9',
            'Cookie': cookieString,
            'Host': 'pay.nwnu.edu.cn',
            'Referer': `https://pay.nwnu.edu.cn/BillView.aspx?ptid=${ptid}`,
            'Content-Type': 'application/json;charset=UTF-8',
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 Edg/135.0.0.0'
          }
        };
        try {
          const response = await httpRequest(options, {
            gType: 'zf',
            pid: ptid,
            sflx: 'other'
          })
          if (!response?.Content || !response?.Content?.length) {
            return callback(index, '未找到发票')
          }
          const base64Image = response.Content[0]?.ImgUrl
          if (!base64Image) {
            return callback(index, '未找到发票')
          }
          const format = base64Image.match(/^data:image\/(\w+);base64,/)[1]
          // 1. 移除 Base64 前缀
          const base64Data = base64Image.replace(/^data:image\/\w+;base64,/, '')
          // 2. 解码为 Buffer
          const imageBuffer = Buffer.from(base64Data, 'base64')
          // 路径安全校验
          const sanitizedDir = path.resolve(savePath)
          // 创建目录（如果不存在）
          if (!fs.existsSync(sanitizedDir)) {
            fs.mkdirSync(sanitizedDir, { recursive: true })
          }
          // if (!sanitizedDir.startsWith(process.cwd())) {
          //   callback(index, '路径超出允许范围')
          //   throw new Error('路径超出允许范围')
          // }
          // 3. 构造保存路径
          const fullPath = path.join(savePath, `${name}.${format}`)
          // 4. 写入文件（同步写入示例）
          fs.writeFileSync(fullPath, imageBuffer)
          callback(index, 'success')
        } catch (err: any) {
          callback(index, err.message || '图片保存失败')
          console.error(err)
        }
      } else {
        // 定位验证码元素
        const captchaElement = await page.$('#Image1')
        if (!captchaElement) {
          console.log('未找到验证码元素')
          retryCount++
          continue
        }
        // 截图并保存为 Buffer
        const imageBuffer = await captchaElement.screenshot({
          encoding: 'binary' // 或 'base64'
        })
        const worker = await createWorker('eng')
        await worker.setParameters({
          tessedit_char_whitelist: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ',
          // tessedit_pageseg_mode: '7'
        })
        const { data: { text: captcha } } = await worker.recognize(Buffer.from(imageBuffer))
        console.log('captcha: ', captcha)
        // await sharp(Buffer.from(imageBuffer))
        //   .toFormat('jpg')
        //   .toFile('temp.jpg')
        // 登录操作
        await page.$eval('#txt_yhm', (el: any) => el.value = '')
        await page.$eval('#txt_pwd', (el: any) => el.value = '')
        await page.$eval('#txt_yzm', (el: any) => el.value = '')
        await page.type('#txt_yhm', username);
        await page.type('#txt_pwd', password);
        await page.type('#txt_yzm', captcha.replace(/[^a-zA-Z0-9]/g, ''))
        // await page.type('#txt_yzm', '2333')
        await page.click('#ImageButton1')

        // 等待页面跳转完成
        const navigationPromise = page.waitForNavigation({ timeout: 2000 })
        await Promise.race([navigationPromise])

        if (alertMessage !== '验证码错误') {
          alertMessage = null
          callback(index, alertMessage || '登录错误')
          throw new Error(alertMessage || '登录错误')
        }

        if (alertMessage) {
          callback(index, '验证码识别失败，重试中')
          await page.click('#Image1')
          await sleepTime(100)
          await page.click('#Image1')
          await sleepTime(500)
          retryCount++
          alertMessage = null
        }
      }
    } catch (error) {
      console.log('error: ', error)
      retryCount++
      callback(index, '验证码识别失败，重试中')
      if (retryCount === MAX_RETRIES) {
        callback(index, 'error')
        console.log(`${name}发票下载失败`)
        // errorList.push(name)
        // await page.close()
      }
    }
  }
}

function sleepTime(time: number) {
  return new Promise((resolve) => {
    setTimeout(resolve, time)
  })
}

function httpRequest(options: http.RequestOptions, data: any): Promise<any> {
  return new Promise((resolve, reject) => {
    const req = http.request(options, (res) => {
      let rawData = '';
      res.on('data', chunk => rawData += chunk);
      res.on('end', () => resolve(JSON.parse(rawData)));
    });
    req.on('error', reject);
    if (data) req.write(JSON.stringify(data));
    req.end();
  });
}
